#include "sprite_bin.h"

#include "graphics/image.h"
#include "graphics/graphics.h"


#include "util/mmgr.h"				// Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"				// logging
#include <cassert>
#include <math.h>
using namespace ui;
#include <stdlib.h> 

static int					VertC = 0;

SpriteBin::SpriteBin() :
	m_SpriteC(0)
{
	memset(m_SpriteA, 0, sizeof(m_SpriteA));
}

SpriteBin::~SpriteBin()
{

}


void				SpriteBin::AddSprite(UISprite* pSprite, const math::Vec2& Pos)
{
	assert(m_SpriteC < MAX_SPRITES);
	assert(pSprite);

	m_SpriteA[m_SpriteC].pSprite = pSprite;
	m_SpriteA[m_SpriteC].Clipped = false;
	m_SpriteA[m_SpriteC].Pos = Pos;
	m_SpriteA[m_SpriteC].Rot = pSprite->m_Rotation;
	m_SpriteA[m_SpriteC].Rotated = (fabs(pSprite->m_Rotation) > 0.0f);

	m_SpriteC++;
}


void				SpriteBin::AddSprite(UISprite* pSprite, float Rot)
{
	if (pSprite)
	{
		pSprite->m_Rotation = Rot;
		AddSprite(pSprite, pSprite->m_Pos);
	}
}


void				SpriteBin::AddClippedSprite(UISprite* pSprite, const math::Vec2& Min, const math::Vec2& Max)
{
	assert(m_SpriteC < MAX_SPRITES);
	assert(pSprite);

	// check if it's totally outside?
	
	m_SpriteA[m_SpriteC].pSprite = pSprite;
	m_SpriteA[m_SpriteC].Clipped = true;
	m_SpriteA[m_SpriteC].Rotated = false;
	m_SpriteA[m_SpriteC].Pos = pSprite->m_Pos;
	m_SpriteA[m_SpriteC].ClipMax = Max;
	m_SpriteA[m_SpriteC].ClipMin = Min;


	m_SpriteC++;
}


void		FlushBuffer(Graphics* pG, Graphics::Vertex2D* VertA)
{
	if (VertC <= 0) return;
	pG->DrawVertex2D(VertA, VertC, Graphics::PT_TRI_LIST);
	VertC = 0;
}

void		SpriteBin::Render(const math::Vec2& Offset, bool Sort)
{
	if	(m_SpriteC <= 0) 
		return;
	if	(Sort)			
		SortSprites();

	Graphics* pG = GetGraphics();
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();

	// draw sprites
	Image*	pCurrentImg = m_SpriteA[0].pSprite->GetImage();
	pG->SetTexture(0, pCurrentImg);
	for (int i=0; i<m_SpriteC; i++)
	{
		if (m_SpriteA[i].pSprite->GetImage())
		{
			bool	Flush = false;
			if (!pCurrentImg || m_SpriteA[i].pSprite->GetImage()->GetSortVal() != pCurrentImg->GetSortVal())
			{
				FlushBuffer(pG, VertA);
				pCurrentImg = m_SpriteA[i].pSprite->GetImage();
				pG->SetTexture(0, pCurrentImg);
			} else
			if (VertC + 6 > Graphics::MAX_TEMP_VERT)
			{
				FlushBuffer(pG, VertA);
				pG->SetTexture(0, pCurrentImg);
			}

			// add new data
			if (m_SpriteA[i].pSprite)
			{
				m_SpriteA[i].pSprite->m_Pos.x = m_SpriteA[i].Pos.x - Offset.x;
				m_SpriteA[i].pSprite->m_Pos.y = m_SpriteA[i].Pos.y - Offset.y;
				if (m_SpriteA[i].Clipped)
				{
					m_SpriteA[i].pSprite->FillClippedBuffer(&VertA[VertC], m_SpriteA[i].ClipMin, m_SpriteA[i].ClipMax);
				}	
				else if (m_SpriteA[i].Rotated)
				{
					m_SpriteA[i].pSprite->FillRotBuffer(&VertA[VertC], m_SpriteA[i].Rot);
				}
				else
					m_SpriteA[i].pSprite->FillBuffer(&VertA[VertC]);
				VertC+=6;
			}
		}
	}
	FlushBuffer(pG, VertA);

	// clear queue
	m_SpriteC = 0;
}

int			SpriteBin::SortFunc( const void *arg1, const void *arg2 )
{
	// we're just sorting on pointers anyways

	SpriteInfo* Info1 = (SpriteInfo*)arg1;
	SpriteInfo* Info2 = (SpriteInfo*)arg2;

	//if (!Info1->pSprite || !Info2->pSprite) return 0;

	unsigned int	Sort1 = 0; 
	unsigned int	Sort2 = 0;

	if (Info1->pSprite->GetImage())	Sort1 = Info1->pSprite->GetImage()->GetSortVal(); 
/*	if (Info1->pSprite->GetSpriteInst() && Info1->pSprite->GetSpriteInst()->pImage)	
		Sort1 = Info1->pSprite->GetSpriteInst()->pImage->GetSortVal();
*/
	if (Info2->pSprite->GetImage())	Sort2 = Info2->pSprite->GetImage()->GetSortVal(); 
//	if (Info2->pSprite->GetSpriteInst() && Info2->pSprite->GetSpriteInst()->pImage)	
//		Sort2 = Info2->pSprite->GetSpriteInst()->pImage->GetSortVal();

	return (Sort1 - Sort2);
}

void				SpriteBin::SortSprites()
{
	qsort(m_SpriteA, m_SpriteC, sizeof(SpriteInfo), SortFunc);
}